/**
 * @file PlayState.cpp
 *
 * Implementación de PlayState.h
 *
 * @see PlayState.h
 */

#include <PlayState.h>
#include <PauseState.h>
#include <EndState.h>
// #include <EndState.h>
#include <IntroState.h>

template<> PlayState* Ogre::Singleton<PlayState>::ms_Singleton = 0;

PlayState::PlayState()
{
  _pTrackManager = NULL;
  _pSoundFXManager = NULL;
}

PlayState::~PlayState() throw() {}

void PlayState::enter()
{
  _root = Ogre::Root::getSingletonPtr();

  _sceneManager = _root->getSceneManager("SceneManager");
  _raySceneQuery = _sceneManager->createRayQuery(Ogre::Ray());

  _camera = _sceneManager->getCamera("MainCamera");

  _camera->setPosition(Ogre::Vector3(5,20,20));
  _camera->lookAt(Ogre::Vector3(0,0,0));
  _camera->setNearClipDistance(5);
  _camera->setFarClipDistance(10000);


  _window = _root->getAutoCreatedWindow();
  _viewport = _window->getViewport(0);

  _viewport->setBackgroundColour(Ogre::ColourValue(0.0, 0.0, 0.0));

  _overlayManager = Ogre::OverlayManager::getSingletonPtr();
  _overlayManager->getByName("HUD")->show();
  _mouseOverlay = _overlayManager->getOverlayElement("cursor");
  _mouseOverlay->show();
  _lifeOverlay = _overlayManager->getOverlayElement("life");
  _lifeOverlay->show();
  _zlifeOverlay = _overlayManager->getOverlayElement("zlife");

  _overlayManager->getOverlayElement("life")->show();
  _overlayManager->getOverlayElement("screen")->show();
  _overlayManager->getOverlayElement("score")->show();

  _zlifeOverlay->hide();
  _overlayManager->getOverlayElement("screen")->show();
  _overlayManager->getOverlayElement("score")->hide();
  _overlayManager->getOverlayElement("win")->hide();
  _overlayManager->getOverlayElement("lose")->hide();

  _score = 0; // Inicializar puntuacion

  _counter = 0;

  _exitGame = false;

  _pTrackManager = TrackManager::getSingletonPtr();

  if (_pSoundFXManager == NULL)
    _pSoundFXManager = new SoundFXManager;

  _ost = _pTrackManager->load("game.ogg");
  _shoot = _pSoundFXManager->load("disparo.ogg");
  _bite = _pSoundFXManager->load("mordisco.ogg");
  _zombiKill = _pSoundFXManager->load("zombie_muere.ogg");

  _ost->play();

  // Añadidas cosas a partir de aquí.
  _broadphase = new btDbvtBroadphase();
  _collisionConf = new btDefaultCollisionConfiguration();
  _dispatcher = new btCollisionDispatcher(_collisionConf);
  _solver = new btSequentialImpulseConstraintSolver;
  _world = new btDiscreteDynamicsWorld(_dispatcher,_broadphase,
				       _solver,_collisionConf);

  _impulse = _impulseWithoutRotation = btVector3(0, 0, 0);
  _rotation = 0.0;

  _killedCount = 0;

  // Establecimiento propiedades del mundo
  _world->setGravity(btVector3(0,-10,0));

  // Gestores
  _pHumanManager = NULL;
  _pZombieManager = NULL;
  _pStageManager = NULL;
  _pDebugDrawer = NULL;

  // Creacion de los elementos iniciales del mundo
  CreateInitialWorld();
}

void PlayState::exit()
{
  _ost->stop();

  _overlayManager->getOverlayElement("life")->hide();
  _overlayManager->getOverlayElement("zlife")->hide();
  _overlayManager->getOverlayElement("screen")->hide();
  _overlayManager->getOverlayElement("score")->hide();

  _sceneManager->destroyQuery(_raySceneQuery);

  delete _pZombieManager;
  delete _pStageManager;
  delete _pHumanManager;

  delete _world;
  delete _solver;
  delete _collisionConf;
  delete _dispatcher;
  delete _broadphase;
}

void PlayState::pause()
{
  _ost->pause();
}

void PlayState::resume()
{
  _ost->play();
}

bool PlayState::frameStarted(const Ogre::FrameEvent& evt)
{
  Ogre::Real deltaT = evt.timeSinceLastFrame;

  _world->stepSimulation(deltaT, 1); // Actualizar simulación Bullet
  // _pDebugDrawer->step();

  // Actualizar humano
  _pHumanManager->update(0, deltaT);

  // Mover humano
  if (_impulse != btVector3(0, 0, 0))
    _pHumanManager->translate(_impulse.normalize() * 10 * deltaT);

  // Orientar humano
  Ogre::Ray mouseRay = _camera->getCameraToViewportRay
    (_posx/float(_root->getAutoCreatedWindow()->getWidth()),
     _posy/float(_root->getAutoCreatedWindow()->getHeight()));

  std::pair<bool, Ogre::Real> intersection =
    mouseRay.intersects(Ogre::Plane(Ogre::Vector3(0, 1, 0), 0));

  if (intersection.first) {

    Ogre::Vector3 point = mouseRay.getPoint(intersection.second);
    _pHumanManager->lookAt(point);
  }

  // Actualizar zombis
  _pZombieManager->update(0, deltaT);

  // Actualizar HUD
  updateHUD();

  return true;
}

bool PlayState::frameEnded(const Ogre::FrameEvent& evt)
{
  Ogre::Real deltaT = evt.timeSinceLastFrame;
  _world->stepSimulation(deltaT, 1); // Actualizar simulacion Bullet

  return not _exitGame;
}

void PlayState::keyPressed(const OIS::KeyEvent& e)
{
  if (e.key == OIS::KC_ESCAPE)
//    GameManager::getSingletonPtr()->popState();
    _exitGame = true;

  else if (e.key == OIS::KC_P)
    GameManager::getSingletonPtr()->pushState(PauseState::getSingletonPtr());

  switch(e.key)
  {
    case OIS::KC_W:
      _impulseWithoutRotation.setZ(-1);
      break;

    case OIS::KC_S:
      _impulseWithoutRotation.setZ(1);
      break;

    case OIS::KC_A:
      _impulseWithoutRotation.setX(-1);
      break;

    case OIS::KC_D:
      _impulseWithoutRotation.setX(1);
      break;

    default:
      break;
  }

  _impulse = _impulseWithoutRotation.rotate(btVector3(0, 1, 0), _rotation);
}

void PlayState::keyReleased(const OIS::KeyEvent& e)
{
  switch(e.key)
  {
    case OIS::KC_W:
      _impulseWithoutRotation.setZ(0);
      break;

    case OIS::KC_S:
      _impulseWithoutRotation.setZ(0);
      break;

    case OIS::KC_A:
      _impulseWithoutRotation.setX(0);
      break;

    case OIS::KC_D:
      _impulseWithoutRotation.setX(0);
      break;

    case OIS::KC_Q:
      _pHumanManager->human()->yaw(Ogre::Radian(-0.3));
      _rotation -= 0.3;
      break;

    case OIS::KC_E:
      _pHumanManager->human()->yaw(Ogre::Radian(0.3));
      _rotation += 0.3;
      break;

    default:
      break;
  }

  _impulse = _impulseWithoutRotation.rotate(btVector3(0, 1, 0), _rotation);
}

void PlayState::mouseMoved(const OIS::MouseEvent& e)
{
  _posx = e.state.X.abs;
  _posy = e.state.Y.abs;

  _mouseOverlay->setLeft(_posx - _mouseOverlay->getWidth() * 0.5);
  _mouseOverlay->setTop(_posy - _mouseOverlay->getHeight() * 0.5);
}

void PlayState::mousePressed(const OIS::MouseEvent& e, OIS::MouseButtonID id)
{
  if (_pHumanManager->shoot())
  {
//    _zombiKill->play();
    ++_killedCount;
  }

  _shoot->play();
}

void PlayState::mouseReleased(const OIS::MouseEvent& e, OIS::MouseButtonID id) {}

PlayState* PlayState::getSingletonPtr() { return ms_Singleton; }

PlayState& PlayState::getSingleton()
{
  assert(ms_Singleton);
  return *ms_Singleton;
}

void PlayState::CreateInitialWorld()
{

  LoadLevel();

  // DebugDrawer
  // _pDebugDrawer = new BtOgre::DebugDrawer
  //   (_sceneManager->getRootSceneNode(), _world);
  // _world->setDebugDrawer(_pDebugDrawer);
}

void
PlayState::LoadLevel() {

 // Crea el escenario
  _pStageManager = new StageManager(_world, _sceneManager);
  _pStageManager->setSceneShadow();
  _pStageManager->createMoon(); // Luz de la luna

  _pStageManager->createStage("MODULO1.mesh", "COLISION1.mesh");
  _pStageManager->createStage("MODULO2.mesh", "COLISION2.mesh");
  _pStageManager->createStage("MODULO3.mesh", "COLISION3.mesh");
  _pStageManager->createStage("MODULO4.mesh", "COLISION4.mesh");

  // XXX Farolas hardcodeadas

  // Modulo 1
  _pStageManager->createStreetlight(Ogre::Vector3(19.295, 3.3, -2.740));
  _pStageManager->createStreetlight(Ogre::Vector3(14.182, 3.3, -4.427));
  _pStageManager->createStreetlight(Ogre::Vector3(13.489, 3.3, 1.631));
  _pStageManager->createStreetlight(Ogre::Vector3(11.724, 3.3, -11.911));
  _pStageManager->createStreetlight(Ogre::Vector3(8.470, 3.3, -8.178));
  _pStageManager->createStreetlight(Ogre::Vector3(6.728, 3.3, 10.006));
  _pStageManager->createStreetlight(Ogre::Vector3(2.873, 3.3, 17.382));
  _pStageManager->createStreetlight(Ogre::Vector3(-1.858, 3.3, 13.631));
  _pStageManager->createStreetlight(Ogre::Vector3(-4.912, 3.3, -10.062));
  _pStageManager->createStreetlight(Ogre::Vector3(-8.662, 3.3, -12.812));
  _pStageManager->createStreetlight(Ogre::Vector3(-12.172, 3.3, 0.0611));

  // Modulo 2
  _pStageManager->createStreetlight(Ogre::Vector3(52.059, 3.3, 19.647));
  _pStageManager->createStreetlight(Ogre::Vector3(31.753, 3.3, 15.091));
  _pStageManager->createStreetlight(Ogre::Vector3(46.076, 3.3, 12.211));
  _pStageManager->createStreetlight(Ogre::Vector3(52.057, 3.3, 12.015));
  _pStageManager->createStreetlight(Ogre::Vector3(31.739, 3.3, 5.459));
  _pStageManager->createStreetlight(Ogre::Vector3(44.158, 3.3, 2.229));
  _pStageManager->createStreetlight(Ogre::Vector3(57.880, 3.3, 2.555));
  _pStageManager->createStreetlight(Ogre::Vector3(31.746, 3.3, -3.830));
  _pStageManager->createStreetlight(Ogre::Vector3(46.093, 3.3, -7.661));
  _pStageManager->createStreetlight(Ogre::Vector3(57.900, 3.3, -10.065));
  _pStageManager->createStreetlight(Ogre::Vector3(31.736, 3.3, -12.698));

  // Modulo 3
  _pStageManager->createStreetlight(Ogre::Vector3(46.125, 3.3, -29.040));
  _pStageManager->createStreetlight(Ogre::Vector3(57.338, 3.3, -29.040));
  _pStageManager->createStreetlight(Ogre::Vector3(34.847, 3.3, -36.660));
  _pStageManager->createStreetlight(Ogre::Vector3(68.320, 3.3, -47.766));
  _pStageManager->createStreetlight(Ogre::Vector3(46.453, 3.3, -50.880));
  _pStageManager->createStreetlight(Ogre::Vector3(35.366, 3.3, -48.192));
  _pStageManager->createStreetlight(Ogre::Vector3(29.224, 3.3, -53.574));
  _pStageManager->createStreetlight(Ogre::Vector3(60.434, 3.3, -63.652));

  // Modulo 4
  _pStageManager->createStreetlight(Ogre::Vector3(-6-208, 3.3, -50.226));
  _pStageManager->createStreetlight(Ogre::Vector3(-6.221, 3.3, -34.505));

  // Humano
  _pHumanManager = new HumanManager(_world, _sceneManager,
                                    OpenSteer::Vec3(0, 0, 17));

  // Zombis
  _pZombieManager = new ZombieManager(_world, _sceneManager,
                                      _pHumanManager->human());

  // XXX Zombis harcodeados

  // Modulo 1
  _pZombieManager->createSpawner(OpenSteer::Vec3(-9, 0, -8), 2, 20);

  // Modulo 2
  _pZombieManager->createSwarm(OpenSteer::Vec3(31, 0, -11), 8);
  _pZombieManager->createSwarm(OpenSteer::Vec3(53, 0, -4), 9);
  _pZombieManager->createSwarm(OpenSteer::Vec3(37, 0, 6), 10);
  _pZombieManager->createSwarm(OpenSteer::Vec3(48, 0, 14), 11);
  _pZombieManager->createSwarm(OpenSteer::Vec3(28, 0, 22), 12);

  // Modulo 3
  _pZombieManager->createZombie(OpenSteer::Vec3(48, 0, -30));
  _pZombieManager->createZombie(OpenSteer::Vec3(51, 0, -53));
  _pZombieManager->createZombie(OpenSteer::Vec3(60, 0, -61));
  _pZombieManager->createZombie(OpenSteer::Vec3(28, 0, -58));
  _pZombieManager->createZombie(OpenSteer::Vec3(27, 0, -29));
  _pZombieManager->createSwarm(OpenSteer::Vec3(34, 0, -33), 2);
  _pZombieManager->createSwarm(OpenSteer::Vec3(59, 0, -30), 3);
  _pZombieManager->createSwarm(OpenSteer::Vec3(37, 0, -46), 4);

  // Modulo 4
  _pZombieManager->createSpawner(OpenSteer::Vec3(-6, 0, -59), 1, 20);
  _pZombieBoss = _pZombieManager->createBoss(OpenSteer::Vec3(6, 0, -42));
}

void
PlayState::updateHUD() {

  int humanLife = _pHumanManager->human()->getLife() * 10;
  if (humanLife <= 0) {
    humanLife = 0;
    EndState* endState = EndState::getSingletonPtr();
    endState->set(0);
    GameManager::getSingletonPtr()->pushState(endState);
  }

//  else
//    _bite->play();

  std::ostringstream lifeStrStream;
  lifeStrStream << "life_" << humanLife;

  _lifeOverlay->setMaterialName(lifeStrStream.str());

  if (_pZombieBoss != NULL) {

    if (!_zlifeOverlay->isVisible()) {

      if (OpenSteer::Vec3::distance(_pHumanManager->human()->position(),
                                    _pZombieBoss->position()) < 20)
        _zlifeOverlay->show();

    }
    else {
      int bossLife = _pZombieBoss->getLife();
      if (bossLife <= 0) {
        bossLife = 0;
        _pZombieBoss = NULL;

        EndState* endState = EndState::getSingletonPtr();
        endState->set(1);
        GameManager::getSingletonPtr()->pushState(endState);
      }
      else bossLife -= bossLife % 10;

      std::ostringstream bossStrStream;
      bossStrStream << "zlife_" << bossLife;

      _zlifeOverlay->setMaterialName(bossStrStream.str());
    }
  }
}
